package cc.aliza.lib.mongo.core;

import com.mongodb.client.AggregateIterable;
import com.mongodb.client.MongoIterable;
import org.bson.Document;

import java.util.List;

/**
 * 『给时光以生命·给岁月以文明』
 *
 * @author Zhang Jing (byrdkm17@gmail.com).
 */
public class AdvancedDao<T> extends AlizaDao<T> {

    public AdvancedDao(Class<T> clazz) {
        super(clazz);
    }

    public double max(String key) {
        return max(key, new Document());
    }

    public double max(String key, AlizaQuery query) {
        return max(key, query.getCondition());
    }

    private double max(String key, Document query) {
        String reduce = "function(key,values){var max=values[0].value;for(var i=1;i<values.length;i++){if(values[i].value>max){max=values[i].value;}}return {'value':max}}";
        return _count(key, query, reduce);
    }

    public double min(String key) {
        return min(key, new Document());
    }

    public double min(String key, AlizaQuery query) {
        return min(key, query.getCondition());
    }

    private double min(String key, Document query) {
        String reduce = "function(key,values){var min=values[0].value;for(var i=1;i<values.length;i++){if(values[i].value<min){min=values[i].value;}}return {'value':min}}";
        return _count(key, query, reduce);
    }

    public double sum(String key) {
        return sum(key, new Document());
    }

    public double sum(String key, AlizaQuery query) {
        return sum(key, query.getCondition());
    }

    private double sum(String key, Document query) {
        String reduce = "function(key,values){var sum=0;for(var i=1;i<values.length;i++){sum+=values[i].value;}}return {'value':sum}}";
        return _count(key, query, reduce);
    }

    private double _count(String key, Document query, String reduce) {
        if (!exists(query)) {
            return 0;
        }
        MongoIterable<Document> results = mapReduce(String.format("function(){emit('%s',{'value':this.%s});}", key, key), reduce, query);
        Document result = results.iterator().next();
        Document document = (Document) result.get("value");
        return Double.parseDouble(document.get("value").toString());
    }

    public double average(String key) {
        return average(key, new Document());
    }

    public double average(String key, AlizaQuery query) {
        return average(key, query.getCondition());
    }

    private double average(String key, Document query) {
        long count = coll.count(query);
        if (count == 0) {
            return 0;
        }
        return sum(key, query) / count;
    }

    private MongoIterable<Document> mapReduce(String map, String reduce, Document query) {
        return coll.mapReduce(map, reduce).filter(query);
    }

    public AggregateIterable<Document> aggregate(List<Document> pipeline) {
        return coll.aggregate(pipeline);
    }

    public MongoIterable<Document> mapReduce(String map, String reduce) {
        return mapReduce(map, reduce, new Document());
    }

    public MongoIterable<Document> mapReduce(String map, String reduce, AlizaQuery query) {
        return mapReduce(map, reduce, query.getCondition());
    }

    private boolean exists(Document query) {
        return coll.count(query) > 0;
    }

}
